优雅永不过时 ——卡密尔
以下不讨论加入TypeScript 和 JSX/TSX
一、使用 require.context
实现前端工程自动化
require.context
是一个 Webpack 提供的Api,通过执行require.context
函数获取一个特定的上下文,主要是用于实现自动化导入模块。
什么时候用? 当一个js文件里面需要手动引入过多的其他文件时,就可以使用。
在Vue项目开发过程中,我们可能会遇到这些可能会用到require.context
的场景
- 当页面内容较多,我们把页面细化为多个组件,然后再通过
import
引入到index.js
主入口文件中的时候。 - 开发了一些基础组件,然后把所有组件都导入到
index.js
中,然后再将所有组件进行全局注册的时候。 - vuex 分割模块后,将各模块引入到
index.js
主文件中的时候。
对于上述的几个场景,如果我们要导入的文件比较少的情况下,通过 import
一个一个去导入还可以接受,但对于量比较大的情况,就变成了纯体力活,并且每次修改都需要在主入口文件内进行调整。这样做一点都不优雅,这时候我们就可以通过 require.context
去简化这个过程。
以第2条为例,讲一下 require.context
的用法
常规用法
组件通过常规方式全局注册(组件越多越繁琐)
// index.js
import Vue from 'vue'
import BaseIcon from './BaseIcon.vue'
import BaseButton from './BaseButton.vue'
import BaseInput from './BaseInput.vue'
import BaseLoading from './BaseLoading.vue'
const components = [
BaseIcon,
BaseButton,
BaseInput,
BaseLoading
]
components.forEach((component) => {
Vue.component(component.name, component)
})
// --------------分割线--------------
// 而更多的同学可能会像下面这样进行注册
Vue.component('base-icon', BaseIcon)
Vue.component('base-button', BaseButton)
Vue.component('base-input', BaseInput)
使用 require.context
require.context
基本语法
require.context(directory, useSubdirectories, regExp);
directory:要搜索的文件夹目录useSubdirectories:是否还去搜索它的子级目录
regExp:一个匹配要搜索文件的正则表达式。
通过 require.context
注册组件
import Vue from 'vue'
// 这里暂不考虑 JSX 和 TSX
const context = require.context('./', false, /\.vue$/)
context.keys().forEach((key) => {
const component = context(key).default
Vue.component(component.name, component)
})
context.keys() 会返回一个数组,包含所有匹配到的文件的路径context(key)可以获取到对应的文件 .default表示 export default 导出的内容
现在代码简洁了很多,也更加优雅。
二、 v-model
原来可以这么用
v-model
属于语法糖
在用Vue开发前端时,不论使用原生还是封装好的UI库,对于表单组件,一般都会使用到 v-model
,而为了更好的表达 value 属性的用处(类似于单选框、复选框),也会进行适当的自定义。但是对于自定义组件同样可以使用 v-model
。
vue 对 v-model
的基本定义
一个组件上的 v-model
默认是在组件上面定义一个名为 value
的 prop ,同时对外暴露一个名为 input
的事件。
像这样:
<template>
<div class="base-input">
<input type="text" :value="value" @change="handleChange" />
</div>
</template>
<script>
export default {
name: 'BaseInput',
props: {
// 名为value 的属性
value: {
type: String,
default: '',
},
},
methods: {
handleChange(e) {
// 暴露一个input时间
this.$emit('input', e.target.value)
},
},
}
</script>
用起来就是这样:
<BaseInput v-model="something" />
对 v-model
进行自定义
通过设置 model
选项, 以自定义开关组件为例
<template>
<div :class="['base-switch', active && 'base-switch-active']" @click="handleClick">
<div class="base-switch-pie"></div>
</div>
</template>
<script>
export default {
name: 'BaseSwitch',
// 通过model属性自定义 属性名和事件名
model: {
event: 'change',
prop: 'active',
},
props: {
// 虽然自定义了 active属性,仍然要在 props 选项里面声明
active: {
type: Boolean,
default: false,
},
},
methods: {
handleClick() {
// 暴露change事件
this.$emit('change', !this.active)
},
},
}
</script>
这么用:
<BaseSwitch v-model="isActive" />
这样的开关组件是不是更简洁了。
三、使用 .sync
,更优雅的实现数据“双向绑定”
在 Vue 中,props
属性是单向数据传输的,父级的 prop 的更新会影响到子组件,但是反过来不行。可是有些情况,我们需要对prop进行“双向绑定”。
上面,我们提到了使用 v-model
实现“双向绑定”。但有时候我们希望一个组件可以实现多个数据的“双向绑定”,而在 Vue2.x 中 v-model
一个组件只能有一个,这时候就需要使用到 .sync
。
.sync
与 v-model
的异同
相同点:
- 两者的本质都是语法糖,目的都是实现组件与外部数据的“双向绑定”
- 两者都是通过
属性+事件
来实现的
不同点:
- 一个组件只能定义一个
v-model
,但可以定义多个.sync
- 定义方式不同,
v-model
默认事件为input
,可以通过配置model
选项来修改,.sync
事件名称固定为update:属性名
使用 .sync
比如写一个遮罩层
<template>
<div class="base-mask" v-if="showMask" @click.stop="handleClickMask">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'BaseMask',
props: {
showMask: {
type: Boolean,
default: false,
},
},
methods: {
handleClickMask() {
this.$emit('update:showMask', !this.showMask)
},
},
}
</script>
这样去使用
<template>
<div class="about">
<BaseSwitch v-model="isActive" />
<BaseMask :show-mask.sync="isActive">这里可以加一些东西</BaseMask>
</div>
</template>
<script>
export default {
data() {
return {
isActive: false,
}
},
}
</script>
代码简洁的同时,也更加优雅
四、动态组件,更优雅的按条件渲染页面
有这么一个场景,需要根据条件(多条件),在页面显示相应的内容,如果用 v-if
来判断要显示哪个组件,那就会是这个样子:
<template>
<div>
<ComponentA v-if="type === 'a'" />
<ComponentB v-else-if="type === 'b'" />
<ComponentC v-else-if="type === 'c'" />
<!-- 这里还有 v-else-if -->
<ComponentG v-else />
</div>
</template>
条件越多,上面代码中那一堆 v-if
,v-else-if
就越繁琐,时刻记得要优雅,这时候就该动态组件上场了。
<template>
<div>
<button v-for="type in types" :key="type" @click="currentType = type">
{{ type }}
</button>
<component :is="currentComponent"></component>
</div>
</template>
<script>
// 组件不多的情况下手动引入即可
const context = require.context('./', false, /\.vue$/)
let components = {}
context.keys().forEach((key) => {
if (key.includes('index')) return
const component = context(key).default
components[component.name] = component
})
export default {
components,
data() {
return {
typeComponents: components,
types: {},
currentType: '',
}
},
computed: {
currentComponent() {
return this.typeComponents[this.currentType]
},
},
created() {
this.types = Object.keys(this.typeComponents)
this.currentType = this.types[0] // 默认第一个选项
},
}
</script>
五、 mixins
,更高效的实现组件内容的复用
mixins
是 Vue
提供的一种混合混入机制,可以更高效的实现组件内容的复用。
场景示例
在h5页面开发中,安卓端可以通过app提供的api实现像IOS那样,软键盘弹起时不遮挡底部内容,但是并不是任何页面都适用这种情况,所以就可以在需要的页面使用这个功能,并在离开这个页面的时候,关闭这个功能。
但是如果在每个需要的组件里面都去实现一段监听代码,代码重复太多了,此时就可以使用混入来解决这个问题
// 混入代码 upward-mixins.js 简例
export default {
beforeCreate() {
// ...省略
this.$util.needUpwardOnAndroid(true)
},
beforeDestroy() {
// ...省略
this.$util.needUpwardOnAndroid(false)
}
}
在需要使用这种功能的组件中使用即可
<template>
<div></div>
</template>
<script>
import upwardMixins from '../mixins/upward-mixins'
export default {
// mixins 传入一个数组,可以有多个混入对象
mixins: [upwardMixins],
// ...省略
}
</script>
混入规则
在 Vue
中,一个混入对象可以包含任意组件选项,但是对于不同的组件选项,会有不同的使用策略。
-
data
选项,在混入时会进行递归合并,如果两个属性发生冲突,则以组件本身为主const mixin1 = { data() { return { a: 1, b: 2 } } } export default { mixins: [mixin1], data() { return { a: 2 } }, created() { console.log(this.a, this.b) // 输出 2,2 } }
-
生命周期钩子函数
对于生命周期钩子函数,混入时会依次执行。混入对象里面的钩子函数会优先于组件的钩子函数执行。如果一个组件混入了多个对象,对于混入对象里面的同名钩子函数,将按照数组顺序依次执行:
const mixin1 = { created() { console.log('第一个输出') } } const mixin2 = { created() { console.log('第二个输出') } } export default { mixins: [mixin1, mixin2], created() { console.log('第三个输出') } }
- 其他选项,对于值为对象的选项,如
methods
,components
,filter
,directives
,props
等等,会被合并为同一个对象。两个对象键名冲突时,取组件本身对象的键值对。
全局混入
混入也可以进行全局注册。一旦使用全局混入,那么混入的选项将在所有的组件内生效,代码简例:
// 仅作简单的示例
const someMixin = {
// ...一些内容
}
Vue.mixin(someMixin)
请谨慎使用全局混入,因为它会影响每个单独创建的 Vue 实例 (包括第三方组件)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。